package buaa.qel.rpc.client.proxy;

import buaa.qel.rpc.client.asyc.RpcPromise;
import buaa.qel.rpc.client.core.ConnectionManager;
import buaa.qel.rpc.client.handler.RpcClientHandler;
import buaa.qel.rpc.common.enumration.SerializeMethod;
import buaa.qel.rpc.common.message.RpcRequestMessage;
import buaa.qel.rpc.common.util.ServiceKeyMaker;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

@Slf4j
public class RpcProxy<T> implements InvocationHandler {
    private Class<T> clazz;
    private String serviceName;

    public RpcProxy(Class<T> clazz,String serviceName){
        this.clazz = clazz;
        this.serviceName = serviceName;
    }

    @SuppressWarnings("unchecked")
    public <T> T createProxy(){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class == method.getDeclaringClass()) {
            String name = method.getName();
            if ("equals".equals(name)) {
                return proxy == args[0];
            } else if ("hashCode".equals(name)) {
                return System.identityHashCode(proxy);
            } else if ("toString".equals(name)) {
                return proxy.getClass().getName() + "@" +
                        Integer.toHexString(System.identityHashCode(proxy)) +
                        ", with InvocationHandler " + this;
            } else {
                throw new IllegalStateException(String.valueOf(method));
            }
        }
        RpcRequestMessage request = new RpcRequestMessage();
        /**
         * 设置序列化方式，后期考虑设计为动态配置
         */
        request.setSerializeMethod(SerializeMethod.SERIALIZER_KRYO);
        request.setSequenceId(UUID.randomUUID().toString());
        request.setInterfaceName(method.getDeclaringClass().getName());
        request.setServiceName(serviceName);
        request.setMethodName(method.getName());
        request.setParameterTypes(method.getParameterTypes());
        request.setParameterValue(args);
        // Debug
        if (log.isDebugEnabled()) {
            log.debug(method.getDeclaringClass().getName());
            log.debug(method.getName());
            for (int i = 0; i < method.getParameterTypes().length; ++i) {
                log.debug(method.getParameterTypes()[i].getName());
            }
            for (int i = 0; i < args.length; ++i) {
                log.debug(args[i].toString());
            }
        }
        String serviceKey = ServiceKeyMaker.makeServiceKey(method.getDeclaringClass().getName(), serviceName);
        RpcClientHandler handler = ConnectionManager.getInstance().chooseHandler(serviceKey);
        /**
         * 此处业务应该是通过负载均衡获取channel，channel发送请求并获得promise，
         * 后根据promise回收结果
         */
        RpcPromise promise = handler.sendRequestGetPromise(request);
        /**
         * 此处必须在promise任务完成后返回代理方法执行结果，所以采用同步等待模式。
         * sync()会抛出异常
         */
        promise.await();
        if(promise.isSuccess()) {
            return promise.getNow();
        } else {
            log.warn("Rpc execute failed.");
            throw new RuntimeException(promise.cause());
        }
    }
}
